﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Security.Cryptography;
using System.Net.Sockets;
using System.Net;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
using System.ServiceModel;

namespace EtlComm
{
    [Serializable]
    public class LogData
    {
        public int Magic = 1978;
        public string StationID;
        public string TaskID;
        public string ErrorMessage;
        public DateTime EventTime;
        public DispatchProcess ProcessState;
        public int ExtractCount;
        public int LoadCount;
    }

    [Serializable]
    public struct DigitalSignature
    {
        public byte[] data;
        public byte[] hashValue;
        public byte[] signature;
    }
    /// <summary>
    /// 日志基类
    /// </summary>
    public class ClientBaseLog:ServiceBaseLog,IDisposable
    {
        private string public_key = "";
        private string MulticastIP = "";
        private DSACryptoServiceProvider rsa = new DSACryptoServiceProvider();
        UdpClient _udpclient = new UdpClient(AddressFamily.InterNetwork);

        public ClientBaseLog()
        {
            EtlService.EtlServicesClient etlsvr = new EtlService.EtlServicesClient();
            public_key = etlsvr.GetPublicKey();
            MulticastIP = etlsvr.GetMuitlCastAddress();
            _udpclient.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
            _udpclient.Client.Bind(new IPEndPoint(IPAddress.Any, 7824));
            MulticastOption optValue = new MulticastOption(IPAddress.Parse(MulticastIP));
            _udpclient.Client.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, optValue);
            _udpclient.BeginReceive(new AsyncCallback(ReceiveCallBack), _udpclient);
        }

        private void ReceiveCallBack(IAsyncResult ar)
        {
            UdpClient client = ar.AsyncState as UdpClient;
            IPEndPoint ipe = new IPEndPoint(IPAddress.Any, 0);
            byte[] buffer = client.EndReceive(ar, ref ipe);

            if (buffer != null)
            {
                try
                {
                    BinaryFormatter b = new BinaryFormatter();
                    MemoryStream ms = new MemoryStream(buffer);
                    DigitalSignature data = (DigitalSignature)b.Deserialize(ms);
                    if (VerifySignature(data))
                    {
                        byte[] buf = Convert.FromBase64String(Encoding.Default.GetString(data.data));
                        MemoryStream mem_stream = new MemoryStream(buf);
                        LogData logdata = (LogData)b.Deserialize(mem_stream);
                        FireTaskProcess(logdata.StationID, logdata.TaskID, logdata.ProcessState, logdata.ErrorMessage,logdata.ExtractCount,logdata.LoadCount);
                    }
                }
                catch { }
            }
            client.BeginReceive(new AsyncCallback(ReceiveCallBack), client);
        }

        //验证数字签名  
        private bool VerifySignature(DigitalSignature ds)
        {
            DSACryptoServiceProvider dsa = new DSACryptoServiceProvider();
            dsa.ImportCspBlob(Convert.FromBase64String(public_key));
            byte[] remote_HashedValue = ds.hashValue;
            byte[] remote_SignedHash = ds.signature;
            DSASignatureDeformatter sigDeformatter = new DSASignatureDeformatter(dsa);
            sigDeformatter.SetHashAlgorithm("SHA1");

            if (sigDeformatter.VerifySignature(remote_HashedValue, remote_SignedHash))
            {
                return true;
            }

            return false;
        }  

        #region IDisposable 成员

        public void Dispose()
        {
            //调用带参数的Dispose方法，释放托管和非托管资源
            Dispose(true);
            //手动调用了Dispose释放资源，那么析构函数就是不必要的了，这里阻止GC调用析构函数
            GC.SuppressFinalize(this);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="disposing">/传入bool值disposing以确定是否释放托管资源</param>
        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                rsa.Dispose();
                _udpclient.Close();
            }
            else
            {

            }
        }

        #endregion
    }

    public class ServiceBaseLog : ILog
    {
        public string ConnectString { get; set; }

        #region ILog 成员

        public virtual bool WriteLog(string loginfo)
        {
            Debug.WriteLine(loginfo);
            return true;
        }

        public virtual bool WriteLog(string StationID, string TaskID, string loginfo)
        {
            return WriteLog(loginfo);
        }

        public virtual bool WriteLog(string StationID, string TaskID, string loginfo, LogType logtype)
        {
            return WriteLog(loginfo);
        }

        public event EtlProcess OnTaskProcess;

        #endregion

        public virtual void FireTaskProcess(string StationID, string TaskID, DispatchProcess dp, string Messages)
        {
            EtlProcess tempEP = OnTaskProcess;
            if (tempEP != null)
            {
                OnTaskProcess(this, new EtlEventArgs
                {
                    StationID = StationID,
                    TaskID = TaskID,
                    Message = Messages,
                    EventTime = DateTime.Now,
                    DProcess = dp
                });
            }
            if (!WriteLog(StationID, TaskID, Messages))
            {
                throw new ApplicationException("写日志异常...");
            }
        }

        public virtual void FireTaskProcess(string StationID, string TaskID, DispatchProcess dp, string Messages, LogType logtype)
        {
            EtlProcess tempEP = OnTaskProcess;
            if (tempEP != null)
            {
                OnTaskProcess(this, new EtlEventArgs
                {
                    StationID = StationID,
                    TaskID = TaskID,
                    Message = Messages,
                    EventTime = DateTime.Now,
                    DProcess = dp
                });
            }
            if (!WriteLog(StationID, TaskID, Messages,logtype))
            {
                throw new ApplicationException("写日志异常...");
            }
        }

        public virtual void FireTaskProcess(string StationID, string TaskID, DispatchProcess dp, string Messages, int ExtrCount, int LoadCount)
        {
            EtlProcess tempEP = OnTaskProcess;
            if (tempEP != null)
            {
                OnTaskProcess(this, new EtlEventArgs
                {
                    StationID = StationID,
                    TaskID = TaskID,
                    Message = Messages,
                    EventTime = DateTime.Now,
                    DProcess = dp,
                    ExtractCount = ExtrCount,
                    LoadCount = LoadCount
                });
            }
            if (!WriteLog(StationID, TaskID, Messages))
            {
                throw new ApplicationException("写日志异常...");
            }
        }
    }
}
